/*This library evaluates cdee (json) expressions and creates dom structures */
//Author Eddy Spreeuwers

var cdee = (function () {

    var self = this;


    return {

        debug: false,

        log: function (s) {
            if (cdee.debug) console.log(s);
        },

        forEachElm: function (nodeList, callBack, self) {
            //Werkt niet in ie6
            //[].slice.call(nodeList).forEach(callBack);
            //("Nr of elements to process: " +  nodeList.length);
            for (var i = 0; i < nodeList.length; i++) {
                callBack(nodeList.item(i), i, self);
            }

        },

        toType: function (obj) {
            return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
        },

        /*
         "[module]": function () {
         cdee.('--------------------[module]-----------------------');
         },
         */

        findValue: function (map, key) {
            for (m in map) {
                if (m == key) return map[m];
            }
            return null;
        },

        findFunction: function (term) {

            if (cdee[term] !== undefined)
                return term;

            for (var func in cdee) {
                if ((func + "#").charAt(0) === (term + "@").charAt(0)) {
                    return func;
                }
                ;
            }
            ;
            return null;
        },


        "[]": function (term, elm, expr, model, key) {
            cdee.log("iterating " + term);
            var expressionValue = cdee.findValue(expr, term);
            cdee.log("arg expresssion:" + term + " = " + JSON.stringify(expressionValue));
            var iterationSet = eval(term);
            var subExpr = expr[term];
            var m = model;
            for (var i in iterationSet) {
                cdee.log("#" + m + " " + JSON.stringify(subExpr));
                cdee.produce(term, elm, subExpr, m, iterationSet[i]);
            }
        },

        //Use  an expression  enclosed by ( and )  signs to iterate over all its entries
        //example: "(fruits)":{"<TR>":"'hello world!'"}
        "()": function (term, elm, expr, model, key) {
            cdee.log("iterating " + term);
            var expressionValue = cdee.findValue(expr, term);
            var variable = term.replace("(", "").replace(")", "");
            cdee.log("arg expresssion:" + term + " = " + JSON.stringify(expressionValue));
            var iterationSet = eval(variable);
            var subExpr = expr[term];
            var m = model;

            cdee.log("iteration type:" + cdee.toType(iterationSet));
            //Geen strings als charater arrays interpreteren
            if (cdee.toType(iterationSet) === "string") {
                iterationSet = [iterationSet];
            }
            for (var i in iterationSet) {
                cdee.log("#" + i + " " + JSON.stringify(subExpr));
                cdee.produce(term, elm, subExpr, m, iterationSet[i]);
            }
        },

        "*": function (term, elm, expr, model, key) {
            cdee.log("for all:" + JSON.stringify(model));
            var subExpr = expr[term];
            for (m in model) {
                cdee.log("#" + m + " " + JSON.stringify(subExpr));
                cdee.produce(term, elm, subExpr, model[m], m);
            }
        },

        "(model)": function (term, elm, expr, model, key) {
            cdee.log("for all:" + JSON.stringify(model));
            var subExpr = expr[term];
            for (m in model) {
                cdee.log("#" + m + " " + JSON.stringify(subExpr));
                cdee.produce(term, elm, subExpr, model[m], m);
            }
        },


        //Use  (childs)  to iterate over the child elements of an HTML element
        //example: "$(childs)":{"$innerHTML":"'hello world!'"}
        "(childs)": function (term, elm, expr, model, key) {
            //cdee.log("for all child elements:" + JSON.stringify(elm.children));
            var subExpr = expr[term];
            var children = elm.children;
            for (var i = 0; i < elm.children.length; i++) {
                cdee.log("#" + i + " " + JSON.stringify(subExpr));
                cdee.produce(term, elm.children.item(i), subExpr, model, i);
            }
        },

        //Use  the $ sign to sett a property on an HTML element
        //example: "$innerHTML":"'hello world!'"
        "$": function (term, elm, expr, model, key) {

            var property = term.substring(1);
            var expressionValue = cdee.findValue(expr, term);
            cdee.log("setting property: " + property + "=" + expressionValue);
            elm[property] = eval(expressionValue);
        },

        //Use  the @  sign to set an attribute value on an HTML element
        //example: "@value":"'hello world!'"
        "@": function (term, elm, expr, model, key) {

            var attribute = term.substring(1);
            var expressionValue = cdee.findValue(expr, term);
            var value = eval(expressionValue);
            cdee.log("adding attribute " + attribute + "=" + value);
            elm.setAttribute(attribute, value);
            if ("window-bottom" === attribute) {

                var handler = function (target) {
                    return function () {
                        cdee.log('resize:' + target.id);
                        var h = (window.innerHeight - value);
                        if (h < 0) h = 0;
                        target.style.height = h + 'px'
                    }
                };

                cdee.log("handler=" + handler);
                var resizer = handler(elm);
                window.addEventListener("resize", resizer, false);
                resizer();
            }
        },

        //Use  the ~ sign to add a EventListener to the window
        //example: "~":{"resize":"eventHandler"}
        "~": function (term, elm, expr, model, key) {
            var expressionValue = cdee.findValue(expr, term);
            var listeners = eval(expressionValue);
            for (eventName in listeners) {
                //evaluate the expected string value to a handler
                var listener = eval(listeners[eventName]);
                cdee.log("adding eventListener: " + eventName + "=" + listener);
                window.addEventListener(eventName, listener);
            }
        },

        //Use  the ! sign to execute a statement
        //example: "~":{"!":"alert('test')"}
        "!": function (term, elm, expr, model, key) {
            var statement = cdee.findValue(expr, term);
            cdee.log("executing task: " + statement);
            eval(statement);
        },


        //Use  the +  sign to add an eventListener to an HTML element
        //example: "@value":"'hello world!'"
        "+": function (term, elm, expr, model, key) {
            var expressionValue = cdee.findValue(expr, term);
            var listeners = eval(expressionValue);
            for (eventName in listeners) {
                var listener = listeners[eventName];
                cdee.log("adding eventListener: " + eventName + "=" + listener);
                elm.addEventListener(eventName, listener);
            }
        },

        //function for creating elements <>
        //Use   <TAG>  to add a new child (element named TAG)  to an HTML element
        //example: "<BR>":"'hello world!'"
        "<>": function (term, elm, expr, model, key) {
            // element declaration

            cdee.log("creating " + term);
            var expressionValue = cdee.findValue(expr, term);
            cdee.log("arg expresssion:" + term + " = " + JSON.stringify(expressionValue));

            var tag = term.replace("<", "").replace(">", "").replace(/\d/g, "");
            var child = (document.createElement(tag));
            elm.appendChild(child);

            if (cdee.toType(expressionValue) === "string") {
                var value = eval(expressionValue);
                cdee.log("adding expression value: " + value);
                if (child.textContent)
                    child.textContent = "" + value;
                else
                    child.innerText = value;

            } else {
                cdee.log("parent: " + child.tagName);
                cdee.produce(term, child, expressionValue, model, key);
            }
        },


        produce: function (term, element, expression, model, key) {

            var expr = expression;
            var elm = element;
            cdee.log("--------------produce parms------------");
            cdee.log("term        : " + term);
            cdee.log("element name: " + elm.tagName);
            cdee.log("expression:   " + JSON.stringify(expr));
            cdee.log("model data :  " + JSON.stringify(model));
            cdee.log("key: " + key);
            cdee.log("---------------------------------------");

            var produce = cdee.produce;

            var toType = cdee.toType;

            var type = toType(expr);

            //var isString = function(expr){return ( toType(expr)=== "string");};


            cdee.log("type:   " + type);

            if (type === "string") {
                expr = [expr];

            }
            ;

            for (term in expr) {
                cdee.log("term: " + term);
                var func = cdee.findFunction(term);
                if (func != null) {
                    //if a function was found en executed
                    cdee.log("function for:   " + term + " = " + JSON.stringify(func));
                    cdee[func](term, elm, expr, model, key);


                } else {
                    cdee.log("Error unknown function" + term);
                    return;
                }

            }


        },


        process: function (elm, index, elms) {
            var cdeeName = elm.getAttribute("data-cdee");
            var modelName = elm.getAttribute("data-model");
            cdee.log("----- processing element -------");
            cdee.log("element-id: " + elm.id);
            cdee.log("cdeeName: " + cdeeName);
            cdee.log("modelName: " + modelName);
            var expression = eval(cdeeName);
            var model = eval(modelName);


            cdee.log("expresssion: " + cdeeName + "=" + JSON.stringify(expression));
            cdee.log("model data : " + modelName + "=" + JSON.stringify(model));

            // only first time set the content
            elm.originalContent = (elms == null) ? "" : elm.innerHTML;

            cdee.produce("", elm, expression, model);

            elm.refresh = function () {
                this.innerHTML = elm.originalContent;
                cdee.process(this, 0, null);
            }

        },


        execute: function () {
            var elms = document.querySelectorAll("[data-cdee]");
            cdee.forEachElm(elms, cdee.process, null);

        }
    }
}());

if (window.addEventListener)
    window.addEventListener("load", cdee.execute, false);

if (window.attachEvent)
    window.attachEvent("onload", cdee.execute, false);


//Standard expressions
cdee.form = {"<TABLE>": {"(model)": {"<TR>": {"<TD1>": "key", "<TD2>": {"<input>": {"@value": "model"}}}}, "<TR>": {"<TD>": {"@colspan": "2", "<center>": {"<button>": "'OK'"}}}}};
cdee.list = {"(model)": {"<li>": "model"}};
cdee.field = {"<label>": "elm.getAttribute('label')", "<input>": {"@type": "elm.getAttribute('type')", "@value": "elm.getAttribute('var')", "+": {"click": "alert('keyup')"}}};

cdee.header = {"$innerHTML": "'CDEE show case: ' + elm.getAttribute('data-text')"};
cdee['flex-layout'] = {"~": {"resize": "cdee.createResizeHandler(elm)"}, "!": "cdee.createResizeHandler(elm)()"};

cdee.createResizeHandler = function (elm) {
    var _target = elm;
    return function () {
        _target.style.margin = '0px';
        if (_target.parentNode.tagName.toLowerCase() === "body") {
            _target.style.height = (window.innerHeight - 4 * elm.offsetTop ) + "px";
            _target.style.width = (window.innerWidth - 4 * elm.offsetWidth) + "px";
        } else {
            _target.style.height = _target.parentNode.offsetHeight + 'px';
            _target.style.width = _target.parentNode.offsetWidth + 'px';
        }

        var child = 0
        var nrOfChilds = _target.children.length;
        var childOffsetTop = 0;

        if (nrOfChilds > 0) {
            var fixedHeight = _target.children[0].offsetTop;
            var flexElmements = [];
            for (var i = 0; i < nrOfChilds; i++) {
                var child = _target.children[i];
                var flex = child.getAttribute("flex") != null;
                if (flex) {
                    flexElmements.push(child);
                } else {
                    fixedHeight += child.offsetHeight;
                }
            }

            var leftOverHeight = elm.offsetHeight - fixedHeight;
            for (var flexIndex in flexElmements) {
                flexElmements[flexIndex].style.height = (leftOverHeight / flexElmements.length) + 'px';
            }
        }

    };

};
